#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/time.h>
#include "tile2nv12.h"

static void _tile420_nv12_y_msa_90(struct tile420_fmt tile ,struct nv12_fmt* nv12)
{
    int width = MIN(nv12->width,tile.height);  //输出宽
    int height = MIN(nv12->height,tile.width); //输出高
    int aligned_height = height / 16 * 16;
    int residue_height = height - aligned_height;
    int aligned_width = width / 16 * 16;
    int residue_width = width - aligned_width;
    int x = 0;
    for(;x < aligned_width;x += 16) {
        uint8_t *src_y = tile.y + x * tile.stride;
        uint8_t *dst = nv12->d + (height - 16) * nv12->stride + x;
        int y = 0;
        for(;y < aligned_height;y += 16) {
            v16u8 v_src_y[16];
            LOAD_MAC(v_src_y,src_y,0);  LOAD_MAC(v_src_y,src_y,1);  LOAD_MAC(v_src_y,src_y,2);  LOAD_MAC(v_src_y,src_y,3);
            LOAD_MAC(v_src_y,src_y,4);  LOAD_MAC(v_src_y,src_y,5);  LOAD_MAC(v_src_y,src_y,6);  LOAD_MAC(v_src_y,src_y,7);
            LOAD_MAC(v_src_y,src_y,8);  LOAD_MAC(v_src_y,src_y,9);  LOAD_MAC(v_src_y,src_y,10); LOAD_MAC(v_src_y,src_y,11);
            LOAD_MAC(v_src_y,src_y,12); LOAD_MAC(v_src_y,src_y,13); LOAD_MAC(v_src_y,src_y,14); LOAD_MAC(v_src_y,src_y,15);
            ymac_transport_90(v_src_y,dst,nv12->stride);
            src_y += 256;
            dst -= 16 * nv12->stride;
        }
        if(residue_height) {
            v16u8 v_src_y[16];
            uint8_t tmp[256];
            dst = nv12->d + (residue_height - 1) * nv12->stride + x;
            LOAD_MAC(v_src_y,src_y,0);  LOAD_MAC(v_src_y,src_y,1);  LOAD_MAC(v_src_y,src_y,2);  LOAD_MAC(v_src_y,src_y,3);
            LOAD_MAC(v_src_y,src_y,4);  LOAD_MAC(v_src_y,src_y,5);  LOAD_MAC(v_src_y,src_y,6);  LOAD_MAC(v_src_y,src_y,7);
            LOAD_MAC(v_src_y,src_y,8);  LOAD_MAC(v_src_y,src_y,9);  LOAD_MAC(v_src_y,src_y,10); LOAD_MAC(v_src_y,src_y,11);
            LOAD_MAC(v_src_y,src_y,12); LOAD_MAC(v_src_y,src_y,13); LOAD_MAC(v_src_y,src_y,14); LOAD_MAC(v_src_y,src_y,15);
            ymac_transport_90(v_src_y,tmp,16);
            int ix = 0;
            for(;y < height;y++) {
                for(int i = 0;i < 16;i++) {
                    dst[i] = tmp[ix+i];
                }
                ix += 16;
                dst -= nv12->stride;
            }
        }
    }
    if(residue_width) {
        uint8_t *src_y = tile.y + x * tile.stride;
        uint8_t *dst = nv12->d + (height - 16) * nv12->stride + x;
        int y = 0;
        v16u8 v_src_y[16];
        for(;y < aligned_height;y += 16) {

            uint8_t tmp[256];
#define gothrough
            switch(residue_width) {
            case 15:
                LOAD_MAC(v_src_y,src_y,14);
                gothrough;
            case 14:
                LOAD_MAC(v_src_y,src_y,13);
                gothrough;
            case 13:
                LOAD_MAC(v_src_y,src_y,12);
                gothrough;
            case 12:
                LOAD_MAC(v_src_y,src_y,11);
                gothrough;
            case 11:
                LOAD_MAC(v_src_y,src_y,10);
                gothrough;
            case 10:
                LOAD_MAC(v_src_y,src_y,9);
                gothrough;
            case 9:
                LOAD_MAC(v_src_y,src_y,8);
                gothrough;
            case 8:
                LOAD_MAC(v_src_y,src_y,7);
                gothrough;
            case 7:
                LOAD_MAC(v_src_y,src_y,6);
                gothrough;
            case 6:
                LOAD_MAC(v_src_y,src_y,5);
                gothrough;
            case 5:
                LOAD_MAC(v_src_y,src_y,4);
                gothrough;
            case 4:
                LOAD_MAC(v_src_y,src_y,3);
                gothrough;
            case 3:
                LOAD_MAC(v_src_y,src_y,2);
                gothrough;
            case 2:
                LOAD_MAC(v_src_y,src_y,1);
                gothrough;
            case 1:
                LOAD_MAC(v_src_y,src_y,0);
                break;
            default:
                printf("Param Error\n");
                assert(0);
                return ;
            }
            ymac_transport_90(v_src_y,tmp,16);
            int8_t *pdst = dst;
            for(int iy = 0;iy < 16;iy++) {
                for(int ix = 0;ix < residue_width;ix++) {
                    pdst[ix] = tmp[iy * 16 + ix];
                }
                pdst += nv12->stride;
            }
            src_y += 256;
            dst -= 16 * nv12->stride;
        }

        if(residue_height) {
            v16u8 v_src_y[16];
            uint8_t tmp[256];
            dst = nv12->d + (residue_height - 1) * nv12->stride + x;
            switch(residue_width) {
            case 15:
                LOAD_MAC(v_src_y,src_y,14);
                gothrough;
            case 14:
                LOAD_MAC(v_src_y,src_y,13);
                gothrough;
            case 13:
                LOAD_MAC(v_src_y,src_y,12);
                gothrough;
            case 12:
                LOAD_MAC(v_src_y,src_y,11);
                gothrough;
            case 11:
                LOAD_MAC(v_src_y,src_y,10);
                gothrough;
            case 10:
                LOAD_MAC(v_src_y,src_y,9);
                gothrough;
            case 9:
                LOAD_MAC(v_src_y,src_y,8);
                gothrough;
            case 8:
                LOAD_MAC(v_src_y,src_y,7);
                gothrough;
            case 7:
                LOAD_MAC(v_src_y,src_y,6);
                gothrough;
            case 6:
                LOAD_MAC(v_src_y,src_y,5);
                gothrough;
            case 5:
                LOAD_MAC(v_src_y,src_y,4);
                gothrough;
            case 4:
                LOAD_MAC(v_src_y,src_y,3);
                gothrough;
            case 3:
                LOAD_MAC(v_src_y,src_y,2);
                gothrough;
            case 2:
                LOAD_MAC(v_src_y,src_y,1);
                gothrough;
            case 1:
                LOAD_MAC(v_src_y,src_y,0);
                break;
            default:
                printf("Param Error\n");
                assert(0);
                return ;
            }
            ymac_transport_90(v_src_y,tmp,16);
            int ix = 0;
            for(;y < height;y++) {
                for(int i = 0;i < residue_width;i++) {
                    dst[i] = tmp[ix+i];
                }
                ix += 16;
                dst -= nv12->stride;
            }
        }
    }
}

static void _tile420_nv12_y_msa_270(struct tile420_fmt tile ,struct nv12_fmt* nv12)
{
    int width = MIN(nv12->width,tile.height);  //输出宽
    int height = MIN(nv12->height,tile.width); //输出高
    int aligned_height = height / 16 * 16;
    int residue_height = height - aligned_height;
    int aligned_width = width / 16 * 16;
    int residue_width = width - aligned_width;
    /* printf("width: %d\n",width); */
    /* printf("height: %d\n",height); */
    /* printf("nv12->stride: %d\n",nv12->stride); */
    /* printf("residue_width: %d\n",residue_width); */
    /* printf("residue_height: %d\n",residue_height); */
    int x = 0;
    for(;x < aligned_width;x += 16) {
        uint8_t *src_y = tile.y + x * tile.stride;
        uint8_t *dst = nv12->d + (width - 16 - x);
        int y = 0;
        for(;y < aligned_height;y += 16) {
            v16u8 v_src_y[16];
            LOAD_MAC(v_src_y,src_y,0);  LOAD_MAC(v_src_y,src_y,1);  LOAD_MAC(v_src_y,src_y,2);  LOAD_MAC(v_src_y,src_y,3);
            LOAD_MAC(v_src_y,src_y,4);  LOAD_MAC(v_src_y,src_y,5);  LOAD_MAC(v_src_y,src_y,6);  LOAD_MAC(v_src_y,src_y,7);
            LOAD_MAC(v_src_y,src_y,8);  LOAD_MAC(v_src_y,src_y,9);  LOAD_MAC(v_src_y,src_y,10); LOAD_MAC(v_src_y,src_y,11);
            LOAD_MAC(v_src_y,src_y,12); LOAD_MAC(v_src_y,src_y,13); LOAD_MAC(v_src_y,src_y,14); LOAD_MAC(v_src_y,src_y,15);
            ymac_transport_270(v_src_y,dst,nv12->stride);
            src_y += 256;
            dst += 16 * nv12->stride;
        }
        if(residue_height) {
            v16u8 v_src_y[16];
            uint8_t tmp[256];
            LOAD_MAC(v_src_y,src_y,0);  LOAD_MAC(v_src_y,src_y,1);  LOAD_MAC(v_src_y,src_y,2);  LOAD_MAC(v_src_y,src_y,3);
            LOAD_MAC(v_src_y,src_y,4);  LOAD_MAC(v_src_y,src_y,5);  LOAD_MAC(v_src_y,src_y,6);  LOAD_MAC(v_src_y,src_y,7);
            LOAD_MAC(v_src_y,src_y,8);  LOAD_MAC(v_src_y,src_y,9);  LOAD_MAC(v_src_y,src_y,10); LOAD_MAC(v_src_y,src_y,11);
            LOAD_MAC(v_src_y,src_y,12); LOAD_MAC(v_src_y,src_y,13); LOAD_MAC(v_src_y,src_y,14); LOAD_MAC(v_src_y,src_y,15);
            ymac_transport_270(v_src_y,tmp,16);
            int ix = 0;
            for(;y < height;y++) {
                for(int i = 0;i < 16;i++) {
                    dst[i] = tmp[ix+i];
                }
                ix += 16;
                dst += nv12->stride;
            }
        }
    }
    if(residue_width) {
        uint8_t *src_y = tile.y + x * tile.stride;
        uint8_t *dst = nv12->d;
        int y = 0;
        v16u8 v_src_y[16];
        for(;y < aligned_height;y += 16) {
            uint8_t tmp[256];
#define gothrough
            switch(residue_width) {
            case 15:
                LOAD_MAC(v_src_y,src_y,14);
                gothrough;
            case 14:
                LOAD_MAC(v_src_y,src_y,13);
                gothrough;
            case 13:
                LOAD_MAC(v_src_y,src_y,12);
                gothrough;
            case 12:
                LOAD_MAC(v_src_y,src_y,11);
                gothrough;
            case 11:
                LOAD_MAC(v_src_y,src_y,10);
                gothrough;
            case 10:
                LOAD_MAC(v_src_y,src_y,9);
                gothrough;
            case 9:
                LOAD_MAC(v_src_y,src_y,8);
                gothrough;
            case 8:
                LOAD_MAC(v_src_y,src_y,7);
                gothrough;
            case 7:
                LOAD_MAC(v_src_y,src_y,6);
                gothrough;
            case 6:
                LOAD_MAC(v_src_y,src_y,5);
                gothrough;
            case 5:
                LOAD_MAC(v_src_y,src_y,4);
                gothrough;
            case 4:
                LOAD_MAC(v_src_y,src_y,3);
                gothrough;
            case 3:
                LOAD_MAC(v_src_y,src_y,2);
                gothrough;
            case 2:
                LOAD_MAC(v_src_y,src_y,1);
                gothrough;
            case 1:
                LOAD_MAC(v_src_y,src_y,0);
                break;
            default:
                printf("Param Error\n");
                assert(0);
                return ;
            }
            ymac_transport_270(v_src_y,tmp,16);
            int8_t *pdst = dst;
            int sx = 16 - residue_width;
            for(int iy = 0;iy < 16;iy++) {
                for(int ix = 0;ix < residue_width;ix++) {
                    pdst[ix] = tmp[iy * 16 + (sx + ix)];
                }
                pdst += nv12->stride;
            }
            src_y += 256;
            dst += 16 * nv12->stride;
        }

        if(residue_height) {
            v16u8 v_src_y[16];
            uint8_t tmp[256];
            switch(residue_width) {
            case 15:
                LOAD_MAC(v_src_y,src_y,14);
                gothrough;
            case 14:
                LOAD_MAC(v_src_y,src_y,13);
                gothrough;
            case 13:
                LOAD_MAC(v_src_y,src_y,12);
                gothrough;
            case 12:
                LOAD_MAC(v_src_y,src_y,11);
                gothrough;
            case 11:
                LOAD_MAC(v_src_y,src_y,10);
                gothrough;
            case 10:
                LOAD_MAC(v_src_y,src_y,9);
                gothrough;
            case 9:
                LOAD_MAC(v_src_y,src_y,8);
                gothrough;
            case 8:
                LOAD_MAC(v_src_y,src_y,7);
                gothrough;
            case 7:
                LOAD_MAC(v_src_y,src_y,6);
                gothrough;
            case 6:
                LOAD_MAC(v_src_y,src_y,5);
                gothrough;
            case 5:
                LOAD_MAC(v_src_y,src_y,4);
                gothrough;
            case 4:
                LOAD_MAC(v_src_y,src_y,3);
                gothrough;
            case 3:
                LOAD_MAC(v_src_y,src_y,2);
                gothrough;
            case 2:
                LOAD_MAC(v_src_y,src_y,1);
                gothrough;
            case 1:
                LOAD_MAC(v_src_y,src_y,0);
                break;
            default:
                printf("Param Error\n");
                assert(0);
                return ;
            }
            ymac_transport_270(v_src_y,tmp,16);
            int ix = 0;
            int sx = 16 - residue_width;
            for(;y < height;y++) {
                for(int i = 0;i < residue_width;i++) {
                    dst[i] = tmp[ix + sx + i];
                }
                ix += 16;
                dst += nv12->stride;
            }
        }
    }
}

static void _tile420_nv12_y_msa_180(struct tile420_fmt tile ,struct nv12_fmt* nv12)
{
    int width = MIN(nv12->width,tile.width);  //输出宽
    int height = MIN(nv12->height,tile.height); //输出高
    int aligned_height = height / 16 * 16;
    int residue_height = height - aligned_height;
    int aligned_width = width / 16 * 16;
    int residue_width = width - aligned_width;
    /* printf("width: %d\n",width); */
    /* printf("height: %d\n",height); */
    /* printf("nv12->stride: %d\n",nv12->stride); */
    /* printf("residue_width: %d\n",residue_width); */
    /* printf("residue_height: %d\n",residue_height); */

    int y = 0;
    for(;y < aligned_height;y += 16) {
        uint8_t *src_y = tile.y + y * tile.stride;
        uint8_t *dst = nv12->d + (height - 16 - y) * nv12->stride + (width - 16);
        int x = 0;
        for(;x < aligned_width;x += 16) {
            v16u8 v_src_y[16];
            LOAD_MAC(v_src_y,src_y,0);  LOAD_MAC(v_src_y,src_y,1);  LOAD_MAC(v_src_y,src_y,2);  LOAD_MAC(v_src_y,src_y,3);
            LOAD_MAC(v_src_y,src_y,4);  LOAD_MAC(v_src_y,src_y,5);  LOAD_MAC(v_src_y,src_y,6);  LOAD_MAC(v_src_y,src_y,7);
            LOAD_MAC(v_src_y,src_y,8);  LOAD_MAC(v_src_y,src_y,9);  LOAD_MAC(v_src_y,src_y,10); LOAD_MAC(v_src_y,src_y,11);
            LOAD_MAC(v_src_y,src_y,12); LOAD_MAC(v_src_y,src_y,13); LOAD_MAC(v_src_y,src_y,14); LOAD_MAC(v_src_y,src_y,15);
            ymac_transport_180(v_src_y,dst,nv12->stride);
            src_y += 256;
            dst -= 16;
        }
        if(residue_width) {
            v16u8 v_src_y[16];
            uint8_t tmp[256];
            dst = nv12->d + (height - 16 - y) * nv12->stride;
            LOAD_MAC(v_src_y,src_y,0);  LOAD_MAC(v_src_y,src_y,1);  LOAD_MAC(v_src_y,src_y,2);  LOAD_MAC(v_src_y,src_y,3);
            LOAD_MAC(v_src_y,src_y,4);  LOAD_MAC(v_src_y,src_y,5);  LOAD_MAC(v_src_y,src_y,6);  LOAD_MAC(v_src_y,src_y,7);
            LOAD_MAC(v_src_y,src_y,8);  LOAD_MAC(v_src_y,src_y,9);  LOAD_MAC(v_src_y,src_y,10); LOAD_MAC(v_src_y,src_y,11);
            LOAD_MAC(v_src_y,src_y,12); LOAD_MAC(v_src_y,src_y,13); LOAD_MAC(v_src_y,src_y,14); LOAD_MAC(v_src_y,src_y,15);
            ymac_transport_180(v_src_y,tmp,16);
            int ix = 16 - residue_width;
            for(int j = 0 ;j < 16;j++) {
                for(int i = 0;i < residue_width;i++) {
                    dst[i] = tmp[ix + i];
                }
                ix += 16;
                dst += nv12->stride;
            }
        }
    }

    if(residue_height) {
        uint8_t *src_y = tile.y + y * tile.stride;
        uint8_t *dst = nv12->d + width - 16;
        v16u8 v_src_y[16];
        int x = 0;
        for(;x < aligned_width;x += 16) {
            uint8_t tmp[256];
#define gothrough
            switch(residue_height) {
            case 15:
                LOAD_MAC(v_src_y,src_y,14);
                gothrough;
            case 14:
                LOAD_MAC(v_src_y,src_y,13);
                gothrough;
            case 13:
                LOAD_MAC(v_src_y,src_y,12);
                gothrough;
            case 12:
                LOAD_MAC(v_src_y,src_y,11);
                gothrough;
            case 11:
                LOAD_MAC(v_src_y,src_y,10);
                gothrough;
            case 10:
                LOAD_MAC(v_src_y,src_y,9);
                gothrough;
            case 9:
                LOAD_MAC(v_src_y,src_y,8);
                gothrough;
            case 8:
                LOAD_MAC(v_src_y,src_y,7);
                gothrough;
            case 7:
                LOAD_MAC(v_src_y,src_y,6);
                gothrough;
            case 6:
                LOAD_MAC(v_src_y,src_y,5);
                gothrough;
            case 5:
                LOAD_MAC(v_src_y,src_y,4);
                gothrough;
            case 4:
                LOAD_MAC(v_src_y,src_y,3);
                gothrough;
            case 3:
                LOAD_MAC(v_src_y,src_y,2);
                gothrough;
            case 2:
                LOAD_MAC(v_src_y,src_y,1);
                gothrough;
            case 1:
                LOAD_MAC(v_src_y,src_y,0);
                break;
            default:
                printf("Param Error\n");
                assert(0);
                return ;
            }
            ymac_transport_180(v_src_y,tmp,16);
            int8_t *pdst = dst;
            int sy = 16 - residue_height;
            for(int iy = 0;iy < residue_height;iy++) {
                for(int ix = 0;ix < 16;ix++) {
                    pdst[ix] = tmp[(sy + iy) * 16 + ix];
                }
                pdst += nv12->stride;
            }
            src_y += 256;
            dst -= 16;
        }

        if(residue_width) {
            v16u8 v_src_y[16];
            uint8_t tmp[256];
            dst = nv12->d;
            switch(residue_height) {
            case 15:
                LOAD_MAC(v_src_y,src_y,14);
                gothrough;
            case 14:
                LOAD_MAC(v_src_y,src_y,13);
                gothrough;
            case 13:
                LOAD_MAC(v_src_y,src_y,12);
                gothrough;
            case 12:
                LOAD_MAC(v_src_y,src_y,11);
                gothrough;
            case 11:
                LOAD_MAC(v_src_y,src_y,10);
                gothrough;
            case 10:
                LOAD_MAC(v_src_y,src_y,9);
                gothrough;
            case 9:
                LOAD_MAC(v_src_y,src_y,8);
                gothrough;
            case 8:
                LOAD_MAC(v_src_y,src_y,7);
                gothrough;
            case 7:
                LOAD_MAC(v_src_y,src_y,6);
                gothrough;
            case 6:
                LOAD_MAC(v_src_y,src_y,5);
                gothrough;
            case 5:
                LOAD_MAC(v_src_y,src_y,4);
                gothrough;
            case 4:
                LOAD_MAC(v_src_y,src_y,3);
                gothrough;
            case 3:
                LOAD_MAC(v_src_y,src_y,2);
                gothrough;
            case 2:
                LOAD_MAC(v_src_y,src_y,1);
                gothrough;
            case 1:
                LOAD_MAC(v_src_y,src_y,0);
                break;
            default:
                printf("Param Error\n");
                assert(0);
                return ;
            }
            ymac_transport_180(v_src_y,tmp,16);
            int ix = (16 - residue_height) * 16 + 16 - residue_width;
            for(int y = 0;y < residue_height;y++) {
                for(int i = 0;i < residue_width;i++) {
                    dst[i] = tmp[ix+i];
                }
                ix += 16;
                dst += nv12->stride;
            }
        }
    }
}



static void _tile420_nv12_uv_msa_90(struct tile420_fmt tile ,struct nv12_fmt* nv12)
{
    uint8_t *p_uv = nv12->d + nv12->height * nv12->stride;
    uint8_t *src_uv = tile.uv;

    int width = MIN(nv12->width,tile.height);  //输出宽
    int height = MIN(nv12->height / 2,tile.width / 2); //输出高
    int aligned_height = height / 8 * 8;
    int residue_height = height - aligned_height;
    int aligned_width = width / 16 * 16;
    int residue_width = width - aligned_width;
    //printf("width: %d\n",width);
    //printf("height: %d\n",height);
    //printf("nv12->stride: %d\n",nv12->stride);
    //printf("residue_width: %d\n",residue_width);
    //printf("residue_height: %d\n",residue_height);
    int x = 0;
    for(;x < aligned_width;x += 16) {
        uint8_t *src = src_uv + x / 2 * tile.stride;
        uint8_t *dst = p_uv + (height - 8) * nv12->stride + x;
        int y = 0;
        for(;y < aligned_height;y += 8) {
            v16u8 v_uv[8];
            LOAD_MAC(v_uv,src,0);  LOAD_MAC(v_uv,src,1);  LOAD_MAC(v_uv,src,2);  LOAD_MAC(v_uv,src,3);
            LOAD_MAC(v_uv,src,4);  LOAD_MAC(v_uv,src,5);  LOAD_MAC(v_uv,src,6);  LOAD_MAC(v_uv,src,7);

            UV_MIX(v_uv);
            uvmac_transport_90(v_uv,dst,nv12->stride);
            src += 128;
            dst -= 8 * nv12->stride;
        }

        if(residue_height) {
            uint8_t tmp[128];
            v16u8 v_uv[8];
            dst = p_uv + (residue_height - 1) * nv12->stride + x;
            LOAD_MAC(v_uv,src,0);  LOAD_MAC(v_uv,src,1);  LOAD_MAC(v_uv,src,2);  LOAD_MAC(v_uv,src,3);
            LOAD_MAC(v_uv,src,4);  LOAD_MAC(v_uv,src,5);  LOAD_MAC(v_uv,src,6);  LOAD_MAC(v_uv,src,7);
            UV_MIX(v_uv);
            uvmac_transport_90(v_uv,tmp,16);
            int ix = 0;
            for(;y < height;y++) {
                for(int i = 0;i < 16;i++) {
                    dst[i] = tmp[ix+i];
                }
                ix += 16;
                dst -= nv12->stride;
            }
        }
    }

    if(residue_width) {
        uint8_t *src = src_uv + x / 2 * tile.stride;
        uint8_t *dst = p_uv + (height - 8) * nv12->stride + x;
        int y = 0;
        v16u8 v_uv[8];
        for(;y < aligned_height;y += 8) {
            uint8_t tmp[128];
#define gothrough
            switch(residue_width / 2) {
            case 7:
                LOAD_MAC(v_uv,src,6);
                gothrough;
            case 6:
                LOAD_MAC(v_uv,src,5);
                gothrough;
            case 5:
                LOAD_MAC(v_uv,src,4);
                gothrough;
            case 4:
                LOAD_MAC(v_uv,src,3);
                gothrough;
            case 3:
                LOAD_MAC(v_uv,src,2);
                gothrough;
            case 2:
                LOAD_MAC(v_uv,src,1);
                gothrough;
            case 1:
                LOAD_MAC(v_uv,src,0);
                break;
            default:
                printf("UV Param Error\n");
                assert(0);
                return ;
            }
            UV_MIX(v_uv);
            uvmac_transport_90(v_uv,tmp,16);
            uint8_t *pdst = dst;
            for(int iy = 0;iy < 8;iy++) {
                for(int ix = 0;ix < residue_width / 2;ix++) {
                    pdst[ix * 2] = tmp[iy * 16 + ix * 2];
                    pdst[ix * 2 + 1] = tmp[iy * 16 + ix * 2 + 1];
                }
                pdst += nv12->stride;
            }
            src += 128;
            dst -= 8 * nv12->stride;
        }
        if(residue_height) {
            v16u8 v_uv[8];
            uint8_t tmp[128];
            dst = p_uv + (residue_height - 1) * nv12->stride + x;
            switch(residue_width) {
            case 7:
                LOAD_MAC(v_uv,src,6);
                gothrough;
            case 6:
                LOAD_MAC(v_uv,src,5);
                gothrough;
            case 5:
                LOAD_MAC(v_uv,src,4);
                gothrough;
            case 4:
                LOAD_MAC(v_uv,src,3);
                gothrough;
            case 3:
                LOAD_MAC(v_uv,src,2);
                gothrough;
            case 2:
                LOAD_MAC(v_uv,src,1);
                gothrough;
            case 1:
                LOAD_MAC(v_uv,src,0);
                break;
            default:
                printf("Param Error\n");
                assert(0);
                return ;
            }
            UV_MIX(v_uv);
            uvmac_transport_90(v_uv,tmp,16);

            int ix = 0;
            for(;y < height;y++) {
                for(int i = 0;i < residue_width;i++) {
                    dst[i * 2] = tmp[ix+i*2];
                    dst[i * 2 + 1] = tmp[ix+i*2 + 1];
                }
                ix += 16;
                dst -= nv12->stride;
            }
        }
    }
}

static void _tile420_nv12_uv_msa_270(struct tile420_fmt tile ,struct nv12_fmt* nv12)
{
    uint8_t *p_uv = nv12->d + nv12->height * nv12->stride;
    uint8_t *src_uv = tile.uv;

    int width = MIN(nv12->width,tile.height);  //输出宽
    int height = MIN(nv12->height / 2,tile.width / 2); //输出高
    int aligned_height = height / 8 * 8;
    int residue_height = height - aligned_height;
    int aligned_width = width / 16 * 16;
    int residue_width = width - aligned_width;
    /* printf("width: %d\n",width); */
    /* printf("height: %d\n",height); */
    /* printf("nv12->stride: %d\n",nv12->stride); */
    /* printf("residue_width: %d\n",residue_width); */
    /* printf("residue_height: %d\n",residue_height); */
    int x = 0;
    for(;x < aligned_width;x += 16) {
        uint8_t *src = src_uv + x / 2 * tile.stride;
        uint8_t *dst = p_uv + width - x - 16;
        int y = 0;
        for(;y < aligned_height;y += 8) {
            v16u8 v_uv[8];
            LOAD_MAC(v_uv,src,0);  LOAD_MAC(v_uv,src,1);  LOAD_MAC(v_uv,src,2);  LOAD_MAC(v_uv,src,3);
            LOAD_MAC(v_uv,src,4);  LOAD_MAC(v_uv,src,5);  LOAD_MAC(v_uv,src,6);  LOAD_MAC(v_uv,src,7);
            UV_MIX(v_uv);
            uvmac_transport_270(v_uv,dst,nv12->stride);
            src += 128;
            dst += 8 * nv12->stride;
        }

        if(residue_height) {
            uint8_t tmp[128];
            v16u8 v_uv[8];
            LOAD_MAC(v_uv,src,0);  LOAD_MAC(v_uv,src,1);  LOAD_MAC(v_uv,src,2);  LOAD_MAC(v_uv,src,3);
            LOAD_MAC(v_uv,src,4);  LOAD_MAC(v_uv,src,5);  LOAD_MAC(v_uv,src,6);  LOAD_MAC(v_uv,src,7);
            UV_MIX(v_uv);
            uvmac_transport_270(v_uv,tmp,16);
            int ix = 0;
            for(;y < height;y++) {
                for(int i = 0;i < 16;i++) {
                    dst[i] = tmp[ix+i];
                }
                ix += 16;
                dst += nv12->stride;
            }
        }
    }

    if(residue_width) {
        uint8_t *src = src_uv + aligned_width / 2 * tile.stride;
        uint8_t *dst = p_uv;
        int y = 0;
        v16u8 v_uv[8];
        for(;y < aligned_height;y += 8) {
            uint8_t tmp[128];
#define gothrough
            switch(residue_width / 2) {
            case 7:
                LOAD_MAC(v_uv,src,6);
                gothrough;
            case 6:
                LOAD_MAC(v_uv,src,5);
                gothrough;
            case 5:
                LOAD_MAC(v_uv,src,4);
                gothrough;
            case 4:
                LOAD_MAC(v_uv,src,3);
                gothrough;
            case 3:
                LOAD_MAC(v_uv,src,2);
                gothrough;
            case 2:
                LOAD_MAC(v_uv,src,1);
                gothrough;
            case 1:
                LOAD_MAC(v_uv,src,0);
                break;
            default:
                printf("UV Param Error\n");
                assert(0);
                return ;
            }
            UV_MIX(v_uv);
            uvmac_transport_270(v_uv,tmp,16);
            uint8_t *pdst = dst;
            int sx = 8 - residue_width / 2;
            for(int iy = 0;iy < 8;iy++) {
                for(int ix = 0;ix < residue_width / 2;ix++) {
                    pdst[ix * 2] = tmp[iy * 16 + (sx + ix) * 2];
                    pdst[ix * 2 + 1] = tmp[iy * 16 + (sx + ix) * 2 + 1];
                }
                pdst += nv12->stride;
            }
            src += 128;
            dst += 8 * nv12->stride;
        }
        if(residue_height) {
            v16u8 v_uv[8];
            uint8_t tmp[128];
            switch(residue_width) {
            case 7:
                LOAD_MAC(v_uv,src,6);
                gothrough;
            case 6:
                LOAD_MAC(v_uv,src,5);
                gothrough;
            case 5:
                LOAD_MAC(v_uv,src,4);
                gothrough;
            case 4:
                LOAD_MAC(v_uv,src,3);
                gothrough;
            case 3:
                LOAD_MAC(v_uv,src,2);
                gothrough;
            case 2:
                LOAD_MAC(v_uv,src,1);
                gothrough;
            case 1:
                LOAD_MAC(v_uv,src,0);
                break;
            default:
                printf("Param Error\n");
                assert(0);
                return ;
            }
            UV_MIX(v_uv);
            uvmac_transport_270(v_uv,tmp,16);

            int ix = 0;
            int sx = 8 - residue_width / 2;
            for(;y < height;y++) {
                for(int i = 0;i < residue_width;i++) {
                    dst[i * 2] = tmp[ix + (sx + i) * 2];
                    dst[i * 2 + 1] = tmp[ix + (sx + i) * 2 + 1];
                }
                ix += 16;
                dst -= nv12->stride;
            }
        }
    }
}


static void _tile420_nv12_uv_msa_180(struct tile420_fmt tile ,struct nv12_fmt* nv12)
{

    uint8_t *p_uv = nv12->d + nv12->height * nv12->stride;
    uint8_t *src_uv = tile.uv;

    int width = MIN(nv12->width,tile.width);  //输出宽
    int height = MIN(nv12->height / 2,tile.height / 2); //输出高
    int aligned_height = height / 8 * 8;
    int residue_height = height - aligned_height;
    int aligned_width = width / 16 * 16;
    int residue_width = width - aligned_width;
    /* printf("width: %d\n",width); */
    /* printf("height: %d\n",height); */
    /* printf("nv12->stride: %d\n",nv12->stride); */
    /* printf("residue_width: %d\n",residue_width); */
    /* printf("residue_height: %d\n",residue_height); */

    int y = 0;
    for(;y < aligned_height;y += 8) {
        uint8_t *src = src_uv + y * tile.stride;
        uint8_t *dst = p_uv + (height - 8 - y) * nv12->stride + (width - 16);
        int x = 0;
        for(;x < aligned_width;x += 16) {
            v16u8 v_uv[8];
            LOAD_MAC(v_uv,src,0);  LOAD_MAC(v_uv,src,1);  LOAD_MAC(v_uv,src,2);  LOAD_MAC(v_uv,src,3);
            LOAD_MAC(v_uv,src,4);  LOAD_MAC(v_uv,src,5);  LOAD_MAC(v_uv,src,6);  LOAD_MAC(v_uv,src,7);
            UV_MIX(v_uv);
            uvmac_transport_180(v_uv,dst,nv12->stride);
            src += 128;
            dst -= 16;
        }
        if(residue_width) {
            v16u8 v_uv[8];
            uint8_t tmp[128];
            dst = p_uv + (height - 8 - y) * nv12->stride;
            LOAD_MAC(v_uv,src,0);  LOAD_MAC(v_uv,src,1);  LOAD_MAC(v_uv,src,2);  LOAD_MAC(v_uv,src,3);
            LOAD_MAC(v_uv,src,4);  LOAD_MAC(v_uv,src,5);  LOAD_MAC(v_uv,src,6);  LOAD_MAC(v_uv,src,7);
            UV_MIX(v_uv);
            uvmac_transport_180(v_uv,tmp,16);

            int ix = 16 - residue_width;
            for(int j = 0 ;j < 8;j++) {
                for(int i = 0;i < residue_width;i++) {
                    dst[i] = tmp[ix + i];
                }
                ix += 16;
                dst += nv12->stride;
            }
        }
    }

    if(residue_height) {
        uint8_t *src = src_uv + y * tile.stride;
        uint8_t *dst = p_uv + width - 16;
        v16u8 v_uv[8];
        int x = 0;
        for(;x < aligned_width;x += 16) {
            uint8_t tmp[128];
            switch(residue_height) {
            case 7:
                LOAD_MAC(v_uv,src,6);
                gothrough;
            case 6:
                LOAD_MAC(v_uv,src,5);
                gothrough;
            case 5:
                LOAD_MAC(v_uv,src,4);
                gothrough;
            case 4:
                LOAD_MAC(v_uv,src,3);
                gothrough;
            case 3:
                LOAD_MAC(v_uv,src,2);
                gothrough;
            case 2:
                LOAD_MAC(v_uv,src,1);
                gothrough;
            case 1:
                LOAD_MAC(v_uv,src,0);
                break;
            default:
                printf("UV Param Error\n");
                assert(0);
                return ;
            }
            UV_MIX(v_uv);
            uvmac_transport_180(v_uv,tmp,16);
            int8_t *pdst = dst;
            int sy = 8 - residue_height;
            for(int iy = 0;iy < residue_height;iy++) {
                for(int ix = 0;ix < 16;ix++) {
                    pdst[ix] = tmp[(sy + iy) * 16 + ix];
                }
                pdst += nv12->stride;
            }
            src += 128;
            dst -= 16;
        }

        if(residue_width) {
            v16u8 v_uv[8];
            uint8_t tmp[128];
            dst = nv12->d;
            switch(residue_height) {
            case 7:
                LOAD_MAC(v_uv,src,6);
                gothrough;
            case 6:
                LOAD_MAC(v_uv,src,5);
                gothrough;
            case 5:
                LOAD_MAC(v_uv,src,4);
                gothrough;
            case 4:
                LOAD_MAC(v_uv,src,3);
                gothrough;
            case 3:
                LOAD_MAC(v_uv,src,2);
                gothrough;
            case 2:
                LOAD_MAC(v_uv,src,1);
                gothrough;
            case 1:
                LOAD_MAC(v_uv,src,0);
                break;
            default:
                printf("UV Param Error\n");
                assert(0);
                return ;
            }
            UV_MIX(v_uv);
            uvmac_transport_180(v_uv,tmp,16);
            int ix = (8 - residue_height) * 16 + 16 - residue_width;
            for(int y = 0;y < residue_height;y++) {
                for(int i = 0;i < residue_width;i++) {
                    dst[i] = tmp[ix+i];
                }
                ix += 16;
                dst += nv12->stride;
            }
        }
    }
}


/*
  tile420 format memory Layout Reference
  docs/tile420-memory-layout.png
  docs/tile420-memory-uv-layout.png
*/
static void _tile420_nv12_y_msa(struct tile420_fmt tile ,struct nv12_fmt* nv12)
{
    int width = MIN(nv12->width,tile.width);
    int height = MIN(nv12->height,tile.height);
    int aligned_height = height / 16 * 16;
    int residue_height = height - aligned_height;
    int aligned_width = width / 16 * 16;
    int residue_width = width - aligned_width;
    uint8_t *dst = nv12->d;
    uint8_t *src_y = tile.y;
    int y = 0;
    for(;y < aligned_height;y += 16) {
        uint8_t *end = dst + aligned_width;
        while(dst < end) {
            v16u8 v_src_y[16];

            LOAD_MAC(v_src_y,src_y,0);  LOAD_MAC(v_src_y,src_y,1);  LOAD_MAC(v_src_y,src_y,2);  LOAD_MAC(v_src_y,src_y,3);
            LOAD_MAC(v_src_y,src_y,4);  LOAD_MAC(v_src_y,src_y,5);  LOAD_MAC(v_src_y,src_y,6);  LOAD_MAC(v_src_y,src_y,7);
            LOAD_MAC(v_src_y,src_y,8);  LOAD_MAC(v_src_y,src_y,9);  LOAD_MAC(v_src_y,src_y,10); LOAD_MAC(v_src_y,src_y,11);
            LOAD_MAC(v_src_y,src_y,12); LOAD_MAC(v_src_y,src_y,13); LOAD_MAC(v_src_y,src_y,14); LOAD_MAC(v_src_y,src_y,15);

            STORE_MAC(v_src_y,dst, 0);  STORE_MAC(v_src_y,dst, 1);  STORE_MAC(v_src_y,dst, 2);  STORE_MAC(v_src_y,dst, 3);
            STORE_MAC(v_src_y,dst, 4);  STORE_MAC(v_src_y,dst, 5);  STORE_MAC(v_src_y,dst, 6);  STORE_MAC(v_src_y,dst, 7);
            STORE_MAC(v_src_y,dst, 8);  STORE_MAC(v_src_y,dst, 9);  STORE_MAC(v_src_y,dst, 10); STORE_MAC(v_src_y,dst, 11);
            STORE_MAC(v_src_y,dst,12);  STORE_MAC(v_src_y,dst,13);  STORE_MAC(v_src_y,dst, 14); STORE_MAC(v_src_y,dst, 15);
            src_y += 256;
            dst += 16;
        }

        if(residue_width) {
            for(int j = 0;j < 16;j++) {
                for(int i = 0;i < residue_width;i++) {
                    dst[j * nv12->stride + i] = src_y[i];
                }
                src_y += 16;
            }
        }

        dst = nv12->d + (y + 16) * nv12->stride;
        src_y = tile.y + (y + 16) * tile.stride;
    }

    if(residue_height) {
        v16u8 v_src_y[1];
        uint8_t *end = dst + aligned_width;
        while(dst < end) {
            uint8_t* m_src_y = src_y;
            for(int j = 0;j < residue_height;j++) {
                LOAD_MAC(v_src_y,m_src_y,0);
                STORE_MAC(v_src_y,dst + j * nv12->stride, 0);
                m_src_y += 16;
            }
            src_y += 16 * 16;
            dst += 16;
        }

        if(residue_width) {
            for(int j = 0;j < residue_height;j++) {
                for(int i = 0;i < residue_width;i++) {
                    dst[j * nv12->stride + i] = src_y[i];
                }
                src_y += 16;
            }
        }
    }
}

static void _tile420_nv12_uv_msa(struct tile420_fmt tile ,struct nv12_fmt* nv12)
{
    uint8_t *p_uv = nv12->d + nv12->height * nv12->stride;
    uint8_t *dst = p_uv;
    uint8_t *src_uv = tile.uv;

    int width = MIN(nv12->width,tile.width);
    int height = MIN(nv12->height / 2,tile.height / 2);

    int aligned_height = height / 8 * 8;
    int residue_height = height - aligned_height;
    int aligned_width = width / 16 * 16;
    int residue_width = width - aligned_width;

    int y = 0;
    for(;y < aligned_height;y += 8) {
        uint8_t *end = dst + aligned_width;
        while(dst < end){
            v16u8 v_uv[8];
            v16u8 v_v[4];
            v16u8 v_u[4];

            LOAD_MAC(v_uv,src_uv,0);  LOAD_MAC(v_uv,src_uv,1);  LOAD_MAC(v_uv,src_uv,2);  LOAD_MAC(v_uv,src_uv,3);
            LOAD_MAC(v_uv,src_uv,4);  LOAD_MAC(v_uv,src_uv,5);  LOAD_MAC(v_uv,src_uv,6);  LOAD_MAC(v_uv,src_uv,7);
            v_u[0] =  __builtin_msa_ilvr_d(v_uv[1],v_uv[0]);
            v_v[0] =  __builtin_msa_ilvl_d(v_uv[1],v_uv[0]);

            v_u[1] =  __builtin_msa_ilvr_d(v_uv[3],v_uv[2]);
            v_v[1] =  __builtin_msa_ilvl_d(v_uv[3],v_uv[2]);

            v_u[2] =  __builtin_msa_ilvr_d(v_uv[5],v_uv[4]);
            v_v[2] =  __builtin_msa_ilvl_d(v_uv[5],v_uv[4]);

            v_u[3] =  __builtin_msa_ilvr_d(v_uv[7],v_uv[6]);
            v_v[3] =  __builtin_msa_ilvl_d(v_uv[7],v_uv[6]);

            v_uv[0] = __builtin_msa_ilvr_b(v_v[0],v_u[0]);
            v_uv[1] = __builtin_msa_ilvl_b(v_v[0],v_u[0]);

            v_uv[2] = __builtin_msa_ilvr_b(v_v[1],v_u[1]);
            v_uv[3] = __builtin_msa_ilvl_b(v_v[1],v_u[1]);

            v_uv[4] = __builtin_msa_ilvr_b(v_v[2],v_u[2]);
            v_uv[5] = __builtin_msa_ilvl_b(v_v[2],v_u[2]);

            v_uv[6] = __builtin_msa_ilvr_b(v_v[3],v_u[3]);
            v_uv[7] = __builtin_msa_ilvl_b(v_v[3],v_u[3]);

            STORE_MAC(v_uv,dst, 0);  STORE_MAC(v_uv,dst, 1);  STORE_MAC(v_uv,dst, 2);  STORE_MAC(v_uv,dst, 3);
            STORE_MAC(v_uv,dst, 4);  STORE_MAC(v_uv,dst, 5);  STORE_MAC(v_uv,dst, 6);  STORE_MAC(v_uv,dst, 7);


            src_uv += 128;
            dst += 16;
        }

        if(residue_width) {
            for(int j = 0;j < 8;j++) {
                for(int i = 0;i < residue_width / 2;i++) {
                    dst[j * nv12->stride + 2 * i] = src_uv[i];
                    dst[j * nv12->stride + 2 * i + 1] = src_uv[16 / 2 + i];
                }
                src_uv += 16;
            }
        }
        dst = p_uv + (y + 8) * nv12->stride;
        src_uv = tile.uv + (y + 8) * tile.stride;
    }

    if(residue_height) {
        uint8_t *end = dst + aligned_width;
        while(dst < end) {
            uint8_t *m_src_uv = src_uv;
            for(int j = 0;j < residue_height;j++) {
                v16u8 v_uv;
                v16u8 v_v;
                v16u8 v_u;
                v_uv = __builtin_msa_ld_b(m_src_uv,0);
                v_u =  __builtin_msa_ilvr_d(v_uv,v_uv);
                v_v =  __builtin_msa_ilvl_d(v_uv,v_uv);
                v_uv = __builtin_msa_ilvr_b(v_v,v_u);
                __builtin_msa_st_b(v_uv,dst + j * nv12->stride,0);
                m_src_uv += 16;
            }
            src_uv += 8 * 16;
            dst += 16;
        }

        if(residue_width) {
            for(int j = 0;j < residue_height;j++) {
                for(int i = 0;i < residue_width / 2;i++) {
                    dst[j * nv12->stride + 2 * i] = src_uv[i];
                    dst[j * nv12->stride + 2 * i + 1] = src_uv[16 / 2 + i];
                }
                src_uv += 16;
            }
        }
    }
}

void _tile420_nv12_msa(struct tile420_fmt src_buf, struct nv12_fmt *dst_buf)
{
    _tile420_nv12_y_msa(src_buf,dst_buf);
    _tile420_nv12_uv_msa(src_buf,dst_buf);
}

void _tile420_nv12_msa_90(struct tile420_fmt src_buf, struct nv12_fmt *dst_buf)
{
    _tile420_nv12_y_msa_90(src_buf,dst_buf);
    _tile420_nv12_uv_msa_90(src_buf,dst_buf);
}

void _tile420_nv12_msa_270(struct tile420_fmt src_buf, struct nv12_fmt *dst_buf)
{
    _tile420_nv12_y_msa_270(src_buf,dst_buf);
    _tile420_nv12_uv_msa_270(src_buf,dst_buf);
}

void _tile420_nv12_msa_180(struct tile420_fmt src_buf, struct nv12_fmt *dst_buf)
{
    _tile420_nv12_y_msa_180(src_buf,dst_buf);
    _tile420_nv12_uv_msa_180(src_buf,dst_buf);
}

